<!--
 * 滚动通知
 * @作者: 陈老师儿~
 * @手机: 18560000860
 -->
<template>
    <view class="pure-notice-bar" :class="classes" :style="styles">
        <view class="pure-notice-bar-wrapper" :id="wrapperId">
            <!-- 左侧 -->
            <view class="pure-notice-bar-left">
                <!-- 兼容京东多slot写法 -->
                <slot name="left" v-if="$slots.left"></slot>
                <view class="pure-notice-bar-icon pure-notice-bar-icon-left" v-else-if="leftIcon">
                    <pure-icon :name="leftIcon"></pure-icon>
                </view>
            </view>

            <!-- 内容 -->
            <view class="pure-notice-bar-content" :id="containerId">
                <!-- 横向 -->
                <view class="pure-notice-bar-items" v-if="!vertical">
                    <view
                        class="pure-notice-bar-item"
                        v-for="(_item, _index) in list"
                        :key="_index"
                        :class="getItemClasses(_index)"
                        @animationend="handleTransitionEnd(_index)"
                        @tap="handleItemClick(_item, _index)"
                        :hover-class="hoverClass"
                    >
                        <text class="pure-notice-bar-item-text" :id="`pure-notice-bar-item-${_index}`">
                            {{ _item[keyName] }}
                        </text>
                    </view>
                </view>
                <!-- 纵向 -->
                <swiper
                    circular
                    class="pure-notice-bar-swiper pure-notice-bar-items"
                    :duration="parseInt(duration)"
                    v-else
                    :interval="verticalInterval"
                    :autoplay="verticalAutoplay"
                    vertical
                    easing-function="linear"
                    :current="index"
                >
                    <swiper-item class="pure-notice-bar-swiper-item" v-for="(_item, _index) in list" :key="_index">
                        <view class="pure-notice-bar-item" @tap="handleItemClick(_item, _index)" :hover-calss="hoverClass">
                            <text class="pure-notice-bar-item-text">
                                {{ _item[keyName] }}
                            </text>
                        </view>
                    </swiper-item>
                </swiper>
            </view>

            <!-- 右侧 -->
            <view class="pure-notice-bar-right">
                <!-- 兼容京东多slot写法 -->
                <slot name="right" v-if="$slots.right"></slot>
                <view class="pure-notice-bar-icon pure-notice-bar-icon-right" v-else-if="rightIcon" :hover-class="hoverClass">
                    <pure-icon :name="rightIcon" @tap="handleRightIconClick"></pure-icon>
                </view>
            </view>
        </view>
    </view>
</template>

<script>
    import props from "./props.js";
    export default {
        options: {
            // 将自定义节点设置成虚拟的
            // 头条、飞书：因为合并后丢失事件和 'provide/inject'
            // 京东：因为合并后不能合并外层 'class'
            // 百度：因为合并后会在控制台疯狂报警告
            // #ifndef MP-TOUTIAO || MP-LARK || MP-JD || MP-BAIDU
            virtualHost: true,
            // #endif

            // 组件样式隔离
            styleIsolation: "apply-shared",

            // 允许使用多个插槽
            multipleSlots: true,
        },
        props: props,
        emits: ["iconCLick", "itemClick"],
        data() {
            return {
                index: -1, // 当前索引
                wrapperId: "", // 容器ID
                containerId: "", // 容器ID
                wrapperRect: null, // 容器节点信息
                containerRect: null, // 容器节点信息
                itemsRect: {}, // items 节点信息
                isInit: false, // 初始化状态
                timer: null, // 定时器
            };
        },
        computed: {
            // classes
            classes() {
                const classes = [];

                // 显示状态
                if (this.show) classes.push("pure-notice-bar-show");

                // 方向
                if (this.vertical) classes.push(`pure-notice-bar-y`);
                else classes.push(`pure-notice-bar-x`);

                // 初始化状态
                if (!this.isInit) classes.push("pure-notice-bar-uninit");

                return classes;
            },

            // styles
            styles() {
                let styles = [];

                // 容器高度
                if (this.wrapperRect?.height) styles.push(`--pure-notice-bar-height: ${this.wrapperRect.height}px;`);

                // 目标位置
                // 横向滚动
                if (!this.vertical) {
                    // 获取 Animation 使用到的变量
                    styles = styles.concat(this.getAnimationVars());
                }

                return styles;
            },

            // 纵向 swiper interval
            verticalInterval() {
                if (this.dock) {
                    if (this.dockTime === "always") return 1000;

                    // 间隔时长
                    let time = parseInt(this.dockTime);

                    // #ifndef MP-ALIPAY
                    // swiper 至少间隔 1000ms，否则会导致无法滚动
                    if (time < 1000) time = 1000;
                    // #endif

                    return time;
                }

                // #ifdef MP-ALIPAY
                return 0;
                // #endif

                return 1000;
            },

            // 纵向 swiper autoplay
            verticalAutoplay() {
                if (this.dockTime === "always") return false;
                return true;
            },
        },
        watch: {
            // 监听 list 变化
            list: {
                handler() {
                    // 重新初始化
                    if (this.show) this.reinit();
                },
                deep: true,
            },

            // 监听 current 变化
            current() {
                // 重新初始化
                if (this.show) this.reinit();
            },

            // 监听显示状态
            show(isShow) {
                if (isShow) {
                    this.init();
                }
            },
        },
        async mounted() {
            // 初始化IDs
            this.initIDs();

            // 初始化
            if (this.show) {
                this.init();
            }

            // 窗口大小变化后
            uni.onWindowResize?.(() => {
                // 初始化
                this.reinit();
            });
        },
        unmounted() {
            // 清除定时器
            this.clearTimer();
        },
        methods: {
            // 初始化IDs
            initIDs() {
                this.wrapperId = `PureNoticeBarWrapper_${Date.now()}_${this.randomString()}`;
                this.containerId = `PureNoticeBarScrollView_${Date.now()}_${this.randomString()}`;
            },

            // 初始化
            async init() {
                // 清除数据
                this.itemsRect = {};

                // 更新初始化状态
                this.isInit = false;

                // 等待数据渲染完成
                await this.$nextTick();
                await this.sleep();

                // 获取容器节点信息
                await this.getWrapperRect();

                // 获取容器节点信息
                await this.getContainerRect();

                // 获取 items 节点信息
                await this.getItemsRect();

                // 默认下标
                this.setIndex(this.current);

                // 更新初始化状态
                this.isInit = true;
            },

            // 重新初始化
            async reinit() {
                // 等待数据渲染完成
                await this.$nextTick();
                await this.sleep();

                // 获取容器节点信息
                await this.getWrapperRect();

                // 获取容器节点信息
                await this.getContainerRect();

                // 获取 items 节点信息
                await this.getItemsRect();

                // 判断下标是否超出范围
                this.setIndex(this.index);
            },

            // 滚动结束
            async handleTransitionEnd() {
                // 停靠模式
                if (this.dock) {
                    // 一直停靠
                    if (this.dockTime === "always") return;

                    // 找到对应元素数据
                    const id = `pure-notice-bar-item-${this.index}`;
                    const item = this.itemsRect[id];

                    // 没找到直接更新下标
                    if (!item) {
                        this.next();
                        return;
                    }

                    // 当前为一阶段
                    if (item?.dockStage === "dock") {
                        // 停靠一下
                        this.clearTimer();
                        this.timer = setTimeout(() => {
                            clearTimeout(this.timer);
                            this.timer = null;
                            // 需执行二阶段
                            item.dockStage = "over";
                        }, this.dockTime);

                        return;
                    }

                    // 当前为二阶段
                    if (item?.dockStage === "over") {
                        this.next();

                        // 恢复成一阶段
                        item.dockStage = "dock";
                        return;
                    }
                }

                // 默认模式
                else {
                    this.next();
                }
            },

            // 获取容器节点信息
            getWrapperRect() {
                return new Promise(async (resolve) => {
                    const query = uni.createSelectorQuery().in(this);
                    query
                        .select(`#${this.wrapperId}`)
                        .boundingClientRect((rect) => {
                            this.wrapperRect = rect;
                            resolve(rect);
                        })
                        .exec();
                });
            },

            // 获取容器节点信息
            getContainerRect() {
                return new Promise(async (resolve) => {
                    const query = uni.createSelectorQuery().in(this);
                    query
                        // #ifdef MP-KUAISHOU
                        // 快手小程序用 containerId 会包含最大子元素的宽度
                        // 所以在快手干脆用 wrapperId
                        .select(`#${this.wrapperId}`)
                        // #endif

                        // #ifndef MP-KUAISHOU
                        .select(`#${this.containerId}`)
                        // #endif
                        .boundingClientRect((rect) => {
                            this.containerRect = rect;
                            resolve(rect);
                        })
                        .exec();
                });
            },

            // 获取 items 节点信息
            getItemsRect() {
                return new Promise(async (resolve) => {
                    const query = uni.createSelectorQuery().in(this);
                    query
                        // #ifdef MP-KUAISHOU
                        // 快手小程序用
                        .selectAll(`#${this.wrapperId} .pure-notice-bar-item-text`)
                        // #endif

                        // #ifndef MP-KUAISHOU
                        .selectAll(`#${this.containerId} .pure-notice-bar-item-text`)
                        // #endif
                        .boundingClientRect((rects) => {
                            // 循环处理
                            rects?.forEach?.((rect) => {
                                // 容器宽度
                                const containerWidth = this.containerRect?.width || 0;

                                // item 宽度
                                const itemWidth = rect.width || 0;

                                // 默认滚动 - 滚动时长
                                const duration = (containerWidth + itemWidth) / parseInt(this.speed);
                                rect.duration = duration * 1000 + "ms";

                                // 停靠滚动 - 滚动阶段
                                // dock: 一阶段 - 停靠在最左侧
                                // over: 二阶段 - 滚动结束
                                const dockStage = this.itemsRect[rect.id]?.dockStage || "dock";
                                rect.dockStage = dockStage;

                                // 停靠滚动 - 一阶段滚动时长
                                const dockDockDuration = containerWidth / parseInt(this.speed);
                                rect.dockDockDuration = dockDockDuration * 1000 + "ms";

                                // 停靠滚动 - 二阶段滚动时长
                                const dockOverDuration = itemWidth / parseInt(this.speed);
                                rect.dockOverDuration = dockOverDuration * 1000 + "ms";

                                // 保存
                                this.itemsRect[rect.id] = rect;
                            });
                            resolve(rects);
                        })
                        .exec();
                });
            },

            // 获取 Animation 使用到的变量
            getAnimationVars() {
                const vars = [];
                const index = this.index;
                const id = `pure-notice-bar-item-${index}`;
                const item = this.itemsRect[id];
                if (!item) return vars;

                // 节点宽度
                vars.push(`--pure-notice-bar-item-moving-width: ${item.width}px;`);

                // 默认模式
                if (!this.dock) {
                    vars.push(`--pure-notice-bar-item-slide-duration: ${item.duration}`);
                    return vars;
                }

                // Dock 模式 一阶段
                if (item.dockStage === "dock") {
                    vars.push(`--pure-notice-bar-item-slide-duration: ${item.dockDockDuration}`);
                    return vars;
                }

                // Dock 模式 二阶段
                if (item.dockStage === "over") {
                    vars.push(`--pure-notice-bar-item-slide-duration: ${item.dockOverDuration}`);
                    return vars;
                }

                return vars;
            },

            // 计算 item classes
            getItemClasses(index) {
                const classes = [];

                // 不是激活项
                if (index !== this.index) return classes;

                // 默认模式
                if (!this.dock) {
                    classes.push("pure-notice-bar-item-moving");
                    return classes;
                }

                // Dock 模式
                const id = `pure-notice-bar-item-${index}`;
                const item = this.itemsRect[id];
                if (!item) return classes;

                // 一阶段
                if (item.dockStage === "dock") {
                    classes.push("pure-notice-bar-item-moving-first");
                    return classes;
                }

                // 二阶段
                if (item.dockStage === "over") {
                    classes.push("pure-notice-bar-item-moving-second");
                    return classes;
                }

                return classes;
            },

            // 下一条
            next() {
                if (this.index >= this.list.length - 1) {
                    this.index = 0;
                } else {
                    this.index = this.index + 1;
                }
            },

            // 设置下标
            setIndex(current) {
                // 超出范围了
                if (current < 0 || current >= this.list.length) {
                    this.index = 0;
                    return;
                }

                // 未超出范围
                this.index = current;
            },

            // 右侧图标点击事件
            handleRightIconClick() {
                this.$emit("iconClick");
            },

            // item 点击事件
            itemClick(item, index) {
                this.$emit("itemClick", item, index);
            },

            // 清除定时器
            clearTimer() {
                if (this.timer) {
                    clearTimeout(this.timer);
                    this.timer = null;
                }
            },

            // Sleep
            sleep(time = 50) {
                return new Promise((resolve) => {
                    let timer = setTimeout(() => {
                        clearTimeout(timer);
                        resolve();
                    }, time);
                });
            },

            // 生成一个36位的随机字符串
            randomString() {
                return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
            },
        },
    };
</script>

<style scoped>
    @import "./style.css";
</style>
